<!DOCTYPE html>
<html>
<head>
<script src="../../resources/js-test.js"></script>
<style>
#computed-infer-vertical-border-spacing { border-spacing: 11px; }
#computed-infer-vertical-border-spacing-from-negative-horizontal-border-spacing { border-spacing: -11px; }
#computed-explicit-border-spacing { border-spacing: 13px 23px; }
#computed-explicit-negative-border-spacing { border-spacing: -5px -13px; }
#computed-explicit-border-spacing-with-negative-horizontal { border-spacing: -5px 13px; }
#computed-explicit-border-spacing-with-negative-vertical { border-spacing: 5px -13px; }
#missing-stylesheet-border-spacing { border-spacing: }
</style>
<script>

window.onload = runTests;

if (!String.prototype.trim) {
    // Assume this script is being executed in Internet Explorer.
    String.prototype.trim = function() {
        return this.replace(/^\s+/, '').replace(/\s+$/, '');
    }
}

function runTests()
{
    debug('Valid values:');
    testInferredVerticalBorderSpacing();
    testExplicitBorderSpacing();
    testComputedInferredVerticalBorderSpacing();
    testComputedExplicitBorderSpacing();

    debug('<br />Negative values:');
    testInferredVerticalBorderSpacingFromNegativeHorizontalSpacing();
    testExplicitNegativeBorderSpacing();
    testExplicitBorderSpacingWithNegativeHorizontal();
    testExplicitBorderSpacingWithNegativeVertical();
    testComputedInferredVerticalBorderSpacingFromNegativeHorizontalSpacing();
    testComputedExplicitNegativeBorderSpacing();
    testComputedExplicitBorderSpacingWithNegativeHorizontal();
    testComputedExplicitBorderSpacingWithNegativeVertical();

    debug('<br />Missing values:');
    testMissingBorderSpacing();
    testMissingStylesheetBorderSpacing();

    document.body.removeChild(document.getElementById("test-container"));
    debug('<br /><span class="pass">TEST COMPLETE</span>');
}

function testInferredVerticalBorderSpacing()
{
    var styleAttr = styleAttribute(document.getElementById("infer-vertical-border-spacing"));
    shouldBeEqualToString('document.getElementById("infer-vertical-border-spacing").style.borderSpacing', parseCSSTextForPropertyValue(styleAttr, "border-spacing"));
}

function testExplicitBorderSpacing()
{
    var styleAttr = styleAttribute(document.getElementById("explicit-border-spacing"));
    shouldBeEqualToString('document.getElementById("explicit-border-spacing").style.borderSpacing', parseCSSTextForPropertyValue(styleAttr, "border-spacing"));
}

function testComputedInferredVerticalBorderSpacing()
{
    shouldBeEqualToString('computedStyle(document.getElementById("computed-infer-vertical-border-spacing")).borderSpacing', "11px 11px");
    shouldBeEqualToString('cssRule("#computed-infer-vertical-border-spacing").style.borderSpacing', "11px"); // Must match border-spacing for #computed-infer-vertical-border-spacing.
}

function testComputedExplicitBorderSpacing()
{
    var expectedResult = "13px 23px"; // Must match border-spacing for #computed-explicit-border-spacing.
    shouldBeEqualToString('computedStyle(document.getElementById("computed-explicit-border-spacing")).borderSpacing', expectedResult);
    shouldBeEqualToString('cssRule("#computed-explicit-border-spacing").style.borderSpacing', expectedResult);
}

function testInferredVerticalBorderSpacingFromNegativeHorizontalSpacing()
{
    // The expected value of the empty string follows from <http://www.w3.org/TR/html5/elements.html#the-style-attribute>,
    // <http://www.w3.org/TR/css-style-attr/#syntax>, <http://www.w3.org/TR/CSS21/syndata.html#declaration>, <http://www.w3.org/TR/CSS21/syndata.html#length-units>,
    // and the definition of "ignore" <http://www.w3.org/TR/CSS21/syndata.html#ignore>.
    shouldBeEqualToString('document.getElementById("infer-vertical-border-spacing-from-negative-horizontal-spacing").style.borderSpacing', "");
}

function testExplicitNegativeBorderSpacing()
{
    // The expected value of the empty string follows from <http://www.w3.org/TR/html5/elements.html#the-style-attribute>,
    // <http://www.w3.org/TR/css-style-attr/#syntax>, <http://www.w3.org/TR/CSS21/syndata.html#declaration>, <http://www.w3.org/TR/CSS21/syndata.html#length-units>,
    // and the definition of "ignore" <http://www.w3.org/TR/CSS21/syndata.html#ignore>.
    shouldBeEqualToString('document.getElementById("explicit-negative-border-spacing").style.borderSpacing', "");
}

function testExplicitBorderSpacingWithNegativeHorizontal()
{
    // The expected value of the empty string follows from <http://www.w3.org/TR/html5/elements.html#the-style-attribute>,
    // <http://www.w3.org/TR/css-style-attr/#syntax>, <http://www.w3.org/TR/CSS21/syndata.html#declaration>, <http://www.w3.org/TR/CSS21/syndata.html#length-units>,
    // and the definition of "ignore" <http://www.w3.org/TR/CSS21/syndata.html#ignore>.
    shouldBeEqualToString('document.getElementById("explicit-border-spacing-with-negative-horizontal").style.borderSpacing', "");
}

function testExplicitBorderSpacingWithNegativeVertical()
{
    // The expected value of the empty string follows from <http://www.w3.org/TR/html5/elements.html#the-style-attribute>,
    // <http://www.w3.org/TR/css-style-attr/#syntax>, <http://www.w3.org/TR/CSS21/syndata.html#declaration>, <http://www.w3.org/TR/CSS21/syndata.html#length-units>,
    // and the definition of "ignore" <http://www.w3.org/TR/CSS21/syndata.html#ignore>.
    shouldBeEqualToString('document.getElementById("explicit-border-spacing-with-negative-vertical").style.borderSpacing', "");
}

function testComputedInferredVerticalBorderSpacingFromNegativeHorizontalSpacing()
{
    // Expected results follow from the explanation given in testInferredVerticalBorderSpacingFromNegativeHorizontalSpacing().
    shouldBeEqualToString('cssRule("#computed-infer-vertical-border-spacing-from-negative-horizontal-border-spacing").style.borderSpacing', "");
}

function testComputedExplicitNegativeBorderSpacing()
{
    // Expected results follow from the explanation given in testExplicitNegativeBorderSpacing().
    shouldBeEqualToString('cssRule("#computed-explicit-negative-border-spacing").style.borderSpacing', "");
}

function testComputedExplicitBorderSpacingWithNegativeHorizontal()
{
    // Expected results follow from the explanation given in testExplicitBorderSpacingWithNegativeHorizontal().
    shouldBeEqualToString('cssRule("#computed-explicit-border-spacing-with-negative-horizontal").style.borderSpacing', "");
}

function testComputedExplicitBorderSpacingWithNegativeVertical()
{
    // Expected results follow from the explanation given in testExplicitBorderSpacingWithNegativeVertical().
    shouldBeEqualToString('cssRule("#computed-explicit-border-spacing-with-negative-vertical").style.borderSpacing', "");
}

function testMissingBorderSpacing()
{
    // Notice, a property with a missing value is considered a malformed declaration by
    // <http://www.w3.org/TR/CSS21/syndata.html#parsing-errors>. Hence, we ignore the declaration.
    shouldBeEqualToString('document.getElementById("missing-border-spacing").style.borderSpacing', "");
}

function testMissingStylesheetBorderSpacing()
{
    // Expected results follow from the explanation given in testMissingBorderSpacing().
    shouldBeEqualToString('cssRule("#missing-stylesheet-border-spacing").style.borderSpacing', "");
}

function styleAttribute(element)
{
    var result = element.getAttribute("style");
    if (typeof(result) === "object") {
        // Assume this script is being executed in Internet Explorer.
        result = result.cssText;
    }
    return result;
}

function computedStyle(element)
{
    if (window.getComputedStyle)
        return window.getComputedStyle(element, null);
    return element.currentStyle; // Assume this script is being executed in Internet Explorer 8 or less.
}

function cssRule(ruleName)
{
    if (!ruleName)
        return;

    if (!document.styleSheets)
        return;

    var NotFound = -1; // We can't make this "const" since IE (as of 8.0) doesn't support it.

    // We normalize the name of the rule to be lowercase since it is case-insensitive by <http://www.w3.org/TR/CSS21/syndata.html#characters>.
    var ruleName = ruleName.toLowerCase();
    var lastRuleThatMatchedCriterion;
    for (var i = 0; i < document.styleSheets.length; ++i) {
        var rules = [];
        try {
            var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules; // IE has "rules" property; WebKit and Firefox have "cssRules". 
        } catch (e) {
            // Firefox's same-origin policy for file URLs prevents access to cssRules for stylesheets that aren't within the
            // same directory as the web page (e.g. ../js/resources/js-test-style.css). So, we ignore such exceptions that arise.
            continue;
        } 
        for (var r = 0; r < rules.length; ++r) {
            if (rules[r].selectorText.toLowerCase() !== ruleName)
                continue;
            lastRuleThatMatchedCriterion = rules[r];
        }
    }
    return lastRuleThatMatchedCriterion;
}

function parseCSSTextForPropertyValue(cssText, propertyName)
{
    if (!cssText)
        return String();

    if (!propertyName)
        return String();

    // Some constants. We can't use "const" since IE (as of 8.0) doesn't support it.
    var Colon = ':';
    var Semicolon = ';';
    var NotFound = -1;

    // Note, CSS property names are case-insensitive, but its corresponding value can be case-
    // sensitive (e.g. font-family) by section 4.1.3 of the CSS 2.1 spec <http://www.w3.org/TR/CSS21/syndata.html#characters>.
    var cssTextLowerCase = cssText.toLowerCase();
    var propertyName = propertyName.toLowerCase();

    var startOfPropertyName = cssTextLowerCase.indexOf(propertyName);
    if (startOfPropertyName === NotFound)
        return;
    var delimiter = cssTextLowerCase.indexOf(Colon, startOfPropertyName);
    if (delimiter === NotFound)
        return;
    if (cssTextLowerCase.substr(startOfPropertyName, delimiter - startOfPropertyName).trim() !== propertyName)
        return;
    var startOfPropertyValue = delimiter + 1;
    var endOfRule = cssTextLowerCase.indexOf(Semicolon, startOfPropertyValue);
    return cssText.substr(startOfPropertyValue, endOfRule === NotFound ? cssText.length : endOfRule - startOfPropertyValue).trim(); // Preserve case-sensitivity of the property value.
}
</script>
</head>
<body>
    <p id="description"></p>
    <div id="test-container">
        <table id="infer-vertical-border-spacing" style="border-spacing: 4px">
            <tr><td>Infer vertical border spacing</td></tr>
        </table>
        <table id="explicit-border-spacing" style="border-spacing: 4px 5px">
            <tr><td>Explicit horizontal and vertical border spacing</td></tr>
        </table>
        <table id="computed-infer-vertical-border-spacing">
            <tr><td>Infer vertical border spacing (uses computed style)</td></tr>
        </table>
        <table id="computed-explicit-border-spacing">
            <tr><td>Explicit horizontal and vertical border spacing (uses computed style)</td></tr>
        </table>

        <table id="infer-vertical-border-spacing-from-negative-horizontal-spacing" style="border-spacing: -5px">
            <tr><td>Infer vertical border spacing from negative horizontal spacing</td></tr>
        </table>
        <table id="explicit-negative-border-spacing" style="border-spacing: -5px -13px">
            <tr><td>Explicit negative horizontal and vertical border spacing</td></tr>
        </table>
        <table id="explicit-border-spacing-with-negative-horizontal" style="border-spacing: -5px 13px">
            <tr><td>Explicit vertical and horizontal border spacing with negative horizontal border spacing</td></tr>
        </table>
        <table id="explicit-border-spacing-with-negative-vertical" style="border-spacing: 5px -13px">
            <tr><td>Explicit vertical and horizontal border spacing with negative vertical border spacing</td></tr>
        </table>
        <table id="computed-infer-vertical-border-spacing-from-negative-horizontal-border-spacing">
            <tr><td>Infer vertical border spacing from negative horizontal border spacing (uses computed style)</td></tr>
        </table>
        <table id="computed-explicit-negative-border-spacing">
            <tr><td>Explicit negative horizontal and vertical border spacing (uses computed style)</td></tr>
        </table>
        <table id="computed-explicit-border-spacing-with-negative-horizontal">
            <tr><td>Explicit horizontal and vertical border spacing with negative horizontal border spacing (uses computed style)</td></tr>
        </table>
        <table id="computed-explicit-border-spacing-with-negative-vertical">
            <tr><td>Explicit horizontal and vertical border spacing with negative vertical border spacing (uses computed style)</td></tr>
        </table>

        <table id="missing-border-spacing" style="border-spacing: ">
            <tr><td>Missing border spacing</td></tr>
        </table>
        <table id="missing-stylesheet-border-spacing">
            <tr><td>Missing border-spacing value in stylesheet</td></tr>
        </table>
    </div>
    <div id="console"></div>
    <script>
        description("This test checks that style.borderSpacing returns the correct result for valid, negative, and missing border-spacing values.");
    </script>
</body>
</html>
